package 函数式编程;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.function.Function;
import java.util.function.IntFunction;
/*
方法引用的标准形式是：类名::方法名。（注意：只需要写方法名，不需要写括号）

有以下四种形式的方法引用：

     类型	                             示例
·引用静态方法                  ContainingClass::staticMethodName
·引用某个对象的实例方法          containingObject::instanceMethodName
·引用某个类型的任意对象的实例方法   ContainingType::methodName
·引用构造方法                  ClassName::new

下面我们通过一个小Demo来分别学习这几种形式的方法引用：

1、静态方法引用

组成语法格式：ClassName::staticMethodName
我们前面举的例子Person::compareByAge就是一个静态方法引用。

注意：
·静态方法引用比较容易理解，和静态方法调用相比，只是把 . 换为 ::
·在目标类型兼容的任何地方，都可以使用静态方法引用。

例子：
　　String::valueOf   等价于lambda表达式 (s) -> String.valueOf(s)
　　Math::pow         等价于lambda表达式  (x,y) -> Math.pow(x,y);

字符串反转的例子：
*/
//interface Function<T, R> {
//   R apply(T t);
/*
interface StringFunc {
    String func(String n);
}

class MyStringOps {
    //静态方法： 反转字符串
    public static String strReverse(String str) {
        String result = "";
        for (int i = str.length() - 1; i >= 0; i--) {
            result += str.charAt(i);
        }
        return result;
    }
}
public class 方法引用2 {
//    public static String stringOp(StringFunc sf, String s) {
//        return sf.func(s);
//    }
    public static String stringOp(Function<String,String> sf, String s) {//亦可  sf可为满足入参：String,返回：String的任意函数实例
        return sf.apply(s);
    }
    public static void main(String[] args) {
        String inStr = "lambda add power to Java";
         
        // 并在接口方法func()中作了MyStringOps.strReverse()操作
        
        //String outStr = stringOp(MyStringOps::strReverse, inStr);//直接lambda静态调用,MyStringOps::strReverse相当于实现了接口方法func()
        String outStr = stringOp(x -> MyStringOps.strReverse(x), inStr);//lambda调用
//        String outStr = stringOp(new StringFunc() {//自定义接口
//        String outStr = stringOp(new Function<String,String>() {//jdk 标准接口
//      	  @Override
//      	  public String apply(String n) {
//      		  return MyStringOps.strReverse(n);
//      	  }
//        }, inStr);
        System.out.println("Original string: " + inStr);
        System.out.println("String reserved: " + outStr);
    }
}
*/
//Original string: lambda add power to Java
//String reserved: avaJ ot rewop dda adbmal
//表达式MyStringOps::strReverse的计算结果为对象引用，其中，strReverse提供了StringFunc的func()方法的实现。
//
//找到列表中具有最大值的对象

/*class MyClass {
    private int val;
    MyClass(int v) {
        val = v;
    }
    public int getValue() {
        return val;
    }
}
//import java.util.ArrayList;
//import java.util.Collections;
//
public class 方法引用2 {
     public static int compareMC(MyClass a, MyClass b) {
         return a.getValue() - b.getValue();
     }
     
     public static void main(String[] args) {
         ArrayList<MyClass> a1 = new ArrayList<MyClass>();
         a1.add(new MyClass(1)); 
         a1.add(new MyClass(4)); 
         a1.add(new MyClass(2));
         a1.add(new MyClass(9));
         a1.add(new MyClass(3));
         a1.add(new MyClass(7));
         //public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) {
         //UseMethodRef::compareMC生成了抽象接口Comparator定义的compare()方法的实例。
         MyClass maxValObj = Collections.max(a1, 方法引用2::compareMC);
         System.out.println("Maximum value is: " + maxValObj.getValue());
    }

}*/

//UseMethodRef定义了静态方法compareMC()，它与Comparator定义的compare()方法兼容。因此，没有必要显式的实现Comparator接口并创建其实例。
//
//2、特定实例对象的方法引用
//这种语法与用于静态方法的语法类似，只不过这里使用对象引用而不是类名。实例方法引用又分以下三种类型：

//《a.实例上的实例方法引用》
//组成语法格式：instanceReference::methodName
//如下示例，引用的方法是myComparisonProvider 对象的compareByName方法。

//class ComparisonProvider{
//    public int compareByName(Person a, Person b){
//        return a.getName().compareTo(b.getName());
//    }
//    public int compareByAge(Person a, Person b){
//        return a.getBirthday().compareTo(b.getBirthday());
//    }
//}
//
//ComparisonProvider myComparisonProvider = new ComparisonProvider();
//Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
//public static <T> void sort(T[] a, Comparator<? super T> c) {


//例子：反转字符串

///**
// * 函数式接口
// */
//interface StringFunc {
//    String func(String n);
//}

//class MyStringOps {
//    //普通方法： 反转字符串
//    public String strReverse1(String str) {
//        String result = "";
//        for (int i = str.length() - 1; i >= 0; i--) {
//            result += str.charAt(i);
//        }
//        return result;
//    }
//}
//public class MethodRefDemo2 {
//    public static String stringOp(StringFunc sf, String s) {
//        return sf.func(s);
//    }
//    public static void main(String[] args) {
//        String inStr = "lambda add power to Java";
//        MyStringOps strOps = new MyStringOps();//实例对象
//        //strOps::strReverse1 相当于实现了接口方法func() 
//        String outStr = stringOp(strOps::strReverse1, inStr);
//
//        System.out.println("Original string: " + inStr);
//        System.out.println("String reserved: " + outStr);
//    }
//}
/*
《b.超类上的实例方法引用》

组成语法格式：super::methodName
方法的名称由methodName指定，通过使用super，可以引用方法的超类版本。

例子：
还可以捕获this 指针，this :: equals  等价于lambda表达式  x -> this.equals(x);

《c.类型上的实例方法引用》

组成语法格式：ClassName::methodName

注意：
若类型的实例方法是泛型的，就需要在::分隔符前提供类型参数，或者（多数情况下）利用目标类型推导出其类型。
静态方法引用和类型上的实例方法引用拥有一样的语法。编译器会根据实际情况做出决定。一般我们不需要指定方法引用中的参数类型，因为编译
器往往可以推导出结果，但如果需要我们也可以显式在::分隔符之前提供参数类型信息。

例子：

String::toString 等价于lambda表达式 (s) -> s.toString()
这里不太容易理解，实例方法要通过对象来调用，方法引用对应Lambda，Lambda的第一个参数会成为调用实例方法的对象。
在泛型类或泛型方法中，也可以使用方法引用。
*/
/*
interface MyFunc<T> {//BiFunction  R apply(T t，U u)
    int func(T[] als, T v);
}

class MyArrayOps {
     public static <T> int countMatching(T[] vals, T v) {
         int count = 0;
         for (int i = 0; i < vals.length; i++) {
             if (vals[i] == v) count++;
         }
         return count;
     }
}

public class 方法引用2 {
    
    public static <T> int myOp(MyFunc<T> f, T[] vals, T v) {
        return f.func(vals, v);
    }
    
    public static void main(String[] args){
        Integer[] vals = {1, 2, 3, 4, 2, 3, 4, 4, 5};
        String[] strs = {"One", "Two", "Three", "Two"};
        int count;
        count=myOp(MyArrayOps::<Integer>countMatching, vals, 4);
        System.out.println("vals contains "+count+" 4s");
        count=myOp(MyArrayOps::<String>countMatching, strs, "Two");
        System.out.println("strs contains "+count+" Twos");
    }

}
*/
//vals contains 3 4s
//strs contains 2 Twos
//分析：
//   在程序中，MyArrayOps是非泛型类，包含泛型方法countMatching()。该方法返回数组中与指定值匹配的元素的个数。注意
//这里如何指定泛型类型参数。例如，在main()方法中，对countMatching()方法的第一次调用如下所
//示：count = myOp(MyArrayOps::<Integer>countMatching,vals,4); 这里传递了类型参数Integer。
//
//   注意，参数传递发生在::的后面。这种语法可以推广。当把泛型方法指定为方法引用时，类型参数出现在::之后、方法名之前。但是，
//需要指出的是，在这种情况(和其它许多情况)下，并非必须显式指定类型参数，因为类型参数会被自动推断得出。对于指定泛型类的情况，
//类型参数位于类名的后面::的前面。
//
//3、任意对象（属于同一个类）的实例方法引用
//
//如下示例，这里引用的是字符串数组中任意一个对象的compareToIgnoreCase方法。
//
//String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
//Arrays.sort(stringArray, String::compareToIgnoreCase);
//4、构造方法引用
//
//构造方法引用又分构造方法引用和数组构造方法引用。
//
//《a.构造方法引用（也可以称作构造器引用）》
//
//组成语法格式：Class::new
//
//构造函数本质上是静态方法，只是方法名字比较特殊，使用的是new 关键字。
//
//例子：
//
//String::new， 等价于lambda表达式 () -> new String() 

interface MyFunc1 {
    MyClass func(int n);
}

class MyClass {
    private int val;
    MyClass(int v) {
        val = v;
    }  
    MyClass(){
        val = 0;
    }
    public int getValue() {
        return val;
    }
}

public class 方法引用2 {
    public static void main(String[] args) {
        MyFunc1 myClassCons = MyClass :: new;
        MyClass mc = myClassCons.func(100);
        System.out.println("val in mc is: " + mc.getValue());
    }
}

//《b.数组构造方法引用》
//
//组成语法格式：TypeName[]::new
//
//例子：
//
//int[]::new 是一个含有一个参数的构造器引用，这个参数就是数组的长度。等价于lambda表达式  x -> new int[x]。
//
//假想存在一个接收int参数的数组构造方法
//
//IntFunction<int[]> arrayMaker = int[]::new;
//int[] array = arrayMaker.apply(10) // 创建数组 int[10]

//<A> A[] toArray(IntFunction<A[]> generator);
